package net.gdface.utils;

import static net.gdface.utils.ConditionChecks.checkNotNull;

import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Map;

/**
 * 实现在实例<T>上代理调用接口<I> <br>
 * @author guyadong
 *
 * @param <I> 接口类型
 */
public class InterfaceNonimplDecorator<I> extends BaseInterfaceDecorator<I,Object>{
	protected final Map<Method, Method> invokeMethods = new Hashtable<>();
	private final boolean lenient;
	/**
	 * 构造方法
	 * 
	 * @param interfaceClass 接口类
	 * @param delegate 实现接口方法的实例
	 * @param lenient 是否为宽容模式---允许接口方法返回类型为void,但对应的代理方法返回类型不为void
	 */
	public InterfaceNonimplDecorator(Class<I> interfaceClass, Object delegate, boolean lenient) {
		super(interfaceClass, checkNotNull(delegate,"delegate is null"));		
		this.lenient = lenient;
		try {
			compile(interfaceClass,delegate);
		} catch (NoSuchMethodException e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 构造方法
	 * 
	 * @param interfaceClass 接口类
	 * @param delegate 实现接口方法的实例
	 */
	public InterfaceNonimplDecorator(Class<I> interfaceClass, Object delegate) {
		this(interfaceClass, delegate, true);
	}
	private static boolean isVoid(Class<?>type){
		return Void.class.equals(type) || void.class.equals(type);
	}
	/**
	 * 创建接口方法与代理对象调用对应的方法的映射,如果找不到抛出异常
	 * @param interfaceClass
	 * @param delegate
	 * @throws NoSuchMethodException
	 */
	protected void compile(Class<I> interfaceClass, Object delegate) throws NoSuchMethodException{
		Class<? extends Object> delegateClass = delegate.getClass();
		for(Method im:interfaceClass.getMethods()){
			// 接口方法参数
			Class<?>[] parameterTypes = im.getParameterTypes();
			// 接口方法返回类型
			Class<?> imReturnType = im.getReturnType();
			// 根据接口方法查找代理类对应的代理方法
			Method dm = delegateClass.getMethod(im.getName(), parameterTypes);
			if(!imReturnType.isAssignableFrom(dm.getReturnType())){
				if(lenient && isVoid(imReturnType)){
					// 允许接口方法返回类型为void,但对应的代理方法返回类型不为void
					// DO NOTHING
				}else	{
					throw new NoSuchMethodException(String.format("Mismatch return type with %s,%s",dm,im));
				}
			}
			invokeMethods.put(im, delegateClass.getMethod(im.getName(), parameterTypes));
		}
	}
	@Override
	protected Object doInvoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object result = invokeMethods.get(method).invoke(delegate, args);
		// 接口方法返回类型为void,但代理方法返回值不为null时,返回null
		return (result != null && isVoid(method.getReturnType())) ? null : result;
	}
}
